﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License, as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Collections.Generic;
using System.Drawing;
using DocumentFormat.OpenXml.Spreadsheet;
using ExtremeML.Core;
using ExtremeML.Packaging;
using ExtremeML.Spreadsheet.Address;
using ExtremeML.Sdk.Vml;

namespace ExtremeML.Spreadsheet
{
	// This is a partial class that adds custom functionality to its auto-generated partner.
	public sealed partial class CommentWrapper
	{
		#region Custom Properties

		/// <summary>
		/// Gets the containing <see cref="WorksheetCommentsPartWrapper"/>.
		/// </summary>
		public WorksheetCommentsPartWrapper WorksheetCommentsPart
		{
			get { return (WorksheetCommentsPartWrapper)Parent.Parent.Parent.Parent; }
		}
	
		/// <summary>
		/// Gets the associated <see cref="WorksheetPartWrapper"/>.
		/// </summary>
		public WorksheetPartWrapper WorksheetPart
		{
			get { return (WorksheetPartWrapper)WorksheetCommentsPart.Parent; }
		}

		/// <summary>
		/// Gets the <see cref="CellAddress"/> that manages cell addressing for the wrapped cell comment.
		/// </summary>
		public CellAddress CellAddress { get; private set; }

		/// <summary>
		/// Gets the <see cref="AuthorWrapper"/> associated with this instance.
		/// </summary>
		public AuthorWrapper Author { get; private set; }

		/// <summary>
		/// Gets or sets the <see cref="CommentShape"/> associated with this instance.
		/// </summary>
		internal CommentShape Shape { get; set; }

		#endregion
		#region Custom Methods

		/// <summary>
		/// Returns the value stored in the text of this instance.
		/// </summary>
		/// <returns>The plain text value of the comment.</returns>
		internal string GetText()
		{
			return CommentText.Text.Value;
		}

		/// <summary>
		/// Sets the text of this instance to the passed value.
		/// </summary>
		/// <param name="value">The value to be assigned to the comment.</param>
		/// <param name="author">An <see cref="AuthorWrapper"/> that represents the author of this comment.</param>
		/// <param name="properties">A <see cref="ShapeProperties"/> that determines presentation of the comment shape.</param>
		internal void SetText(
			string value,
			string author,
			ShapeProperties properties)
		{
			if (Author != null)
			{
				Author.DecrementReferenceCount();
			}

			if (value == null)
			{
                SetImage(null);
                Remove();
			}
			else
			{
				CommentText.Runs.Clear();
				CommentText.Text.Value = value;
				Author = WorksheetCommentsPart.Comments.Authors.GetAuthor(author ?? string.Empty);

				if (properties != null)
				{
					Shape.Configure(properties);
				}
			}
		}

		/// <summary>
		/// Returns the run values stored in the rich text of this instance.
		/// </summary>
		/// <returns>The rich text run values of the comment.</returns>
		internal IEnumerable<Run> GetRichText()
		{
			foreach (var wrapper in CommentText.Runs)
			{
				yield return wrapper.Wrapped;
			}
		}

		/// <summary>
		/// Sets the rich text of this instance to the passed set of run values.
		/// </summary>
		/// <param name="values">The run values to be assigned to the comment.</param>
		/// <param name="author">An <see cref="AuthorWrapper"/> that represents the author of this comment.</param>
		/// <param name="properties">A <see cref="ShapeProperties"/> that determines presentation of the comment shape.</param>
		internal void SetRichText(
			IEnumerable<Run> values,
			string author,
			ShapeProperties properties)
		{
			if (Author != null)
			{
				Author.DecrementReferenceCount();
			}

			if (values == null)
			{
			    SetImage(null);
				Remove();
			}
			else
			{
				CommentText.Runs.Clear();
				CommentText.Text.Value = null;
				CommentText.Runs.AddRange(values);
                Author = WorksheetCommentsPart.Comments.Authors.GetAuthor(author ?? string.Empty);

				if (properties != null)
				{
					Shape.Configure(properties);
				}
			}
		}

        /// <summary>
        /// Returns the shape image underlying this instance.
        /// </summary>
        /// <returns></returns>
        internal Bitmap GetImage()
        {
            return Shape.GetImage();
        }

        /// <summary>
        /// Sets the shape image of this instance to the passed value.
        /// </summary>
        /// <param name="image">A <see cref="Bitmap"/> to be used as the fill image.</param>
        internal void SetImage(
            Bitmap image)
        {
            SetImage(image, null, null);
        }

        /// <summary>
        /// Sets the shape image of this instance to the passed value.
        /// </summary>
        /// <param name="image">A <see cref="Bitmap"/> to be used as the fill image.</param>
        /// <param name="width">A nullable <see cref="int"/> that specifies the rendered image width.</param>
        /// <param name="height">A nullable <see cref="int"/> that specifies the rendered image height.</param>
        internal void SetImage(
            Bitmap image,
            int? width,
            int? height)
        {
            if (image == null)
            {
                Shape.SetImage(null, null, null);
            }
            else
            {
                if (CommentText.Runs.Count == 0 && CommentText.Text.Value == null)
                {
                    // Every comment must have at least an empty text element.
                    SetText(string.Empty, string.Empty, null);                    
                }

                Shape.SetImage(image, width, height);
            }
        }

		#endregion
		#region Overrides

		/// <summary>
		/// Implements custom initialization functionality.
		/// </summary>
		protected override void OnCustomInitializing()
		{
			CellAddress = new CellAddress();
		}

		/// <summary>
		/// Implements custom population functionality.
		/// </summary>
		protected override void OnCustomDecomposing()
		{
			CellAddress = AddressParser.ParseCellAddress(Reference);
		}

		/// <summary>
		/// Establishes relationships with other package content objects.
		/// </summary>
		protected override void OnCustomActivating()
		{
			RegisterAddressDependencies();

			if (AuthorId.HasValue)
			{
				Author = WorksheetCommentsPart.Comments.Authors.GetAuthor((int)AuthorId.Value);
			}

			Shape = WorksheetPart.GetCommentDrawingPart().Drawing.CommentShapes.GetShape(
				WorksheetPart.Worksheet,
				new GridReference(CellAddress.RowIndex.Value, CellAddress.ColumnIndex.Value));

			if (Shape == null)
			{
				Shape = WorksheetPart.GetCommentDrawingPart().Drawing.CommentShapes.GetShape(WorksheetPart.Worksheet);
			}
		}

		/// <summary>
		/// Safely terminates relationships with other package content objects.
		/// </summary>
		protected override void OnCustomDeactivating()
		{
			var cell = new GridReference(CellAddress.RowIndex.Value, CellAddress.ColumnIndex.Value);
			Shape.Configure(new ShapeProperties(cell, null, null));
		}

		/// <summary>
		/// Implements custom persistence functionality.
		/// </summary>
		protected override void OnCustomRecomposing(
			Comment wrapped)
		{
			Reference = CellAddress.Reference;
			AuthorId = (uint)Author.GetIndex();
		}

		/// <summary>
		/// Returns the set of <see cref="IWrapper"/> instances defined as custom properties.
		/// </summary>
		protected override IEnumerable<IWrapper> GetCustomWrapperMembers()
		{
			yield return CellAddress;
		}

		/// <summary>
		/// Implements custom deletion functionality.
		/// </summary>
		protected override void OnCustomRemoved()
		{
			ReleaseAddressDependencies();

			if (Author != null)
			{
				Author.DecrementReferenceCount();
			}

			Shape.Remove();
		}

		#endregion
		#region Implementation Methods

		/// <summary>
		/// Registers addressable contained objects for notification of address changes.
		/// </summary>
		private void RegisterAddressDependencies()
		{
			WorksheetPart.AddressDependencies.Add(CellAddress);

			CellAddress.CellDeleted += HandleCellDeletion;
		}

		/// <summary>
		/// Releases previously registered contained objects from notification of address changes.
		/// </summary>
		private void ReleaseAddressDependencies()
		{
			if (WorksheetPart.AddressDependencies.Contains(CellAddress))
			{
				WorksheetPart.AddressDependencies.Remove(CellAddress);
			}

			CellAddress.CellDeleted -= HandleCellDeletion;
		}

		/// <summary>
		/// Handles the cell deletion event of <see cref="CellAddress"/>.
		/// </summary>
		private void HandleCellDeletion(
			object sender,
			EventArgs e)
		{
			Remove();
		}

		#endregion
		#region Static Methods


		#endregion
	}
}
